package unamur.iodass.communication;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;

import org.apache.log4j.Logger;

import unamur.iodass.auction.AuctionHandler;
import unamur.iodass.catalog.RetreiveAllCatalogs;
import unamur.iodass.communication.messages.BookStore;
import unamur.iodass.communication.messages.CancelDeliver;
import unamur.iodass.communication.messages.Catalog;
import unamur.iodass.communication.messages.ContratBookStore;
import unamur.iodass.communication.messages.LibraryRequest;
import unamur.iodass.communication.messages.Message;
import unamur.iodass.communication.messages.ParcelDelivery;
import unamur.iodass.registration.BookStoreHandler;
import unamur.iodass.registration.ParcelDeliveryHandler;


/**
 * Serveur TCP
 * 
 * @author Cherchem, Marchal
 * Inspiré du code de Dawagne Bruno du projet IALTM Sysdis
 */


public class Server {

	private static final Logger logger = Logger.getLogger(Server.class);
	
	private volatile boolean started;


	/**
	 * Start the listener.
	 */
	void start() {
		started = true;
		Thread daemon = new Thread(new Daemon());
		daemon.start();
	}

	/**
	 * Stop the listener
	 */
	void stop() {
		started = false;
	}
	
	// Represent the daemon lunched when this is started
	private class Daemon implements Runnable {

		@Override
		public void run() {
			try {
				ServerSocket serverSocket = new ServerSocket(Communication.ONLINE_LIBRARY_PORT);
				while (started) {
					try {
						Socket socket = serverSocket.accept();
						new Thread(new Slave(socket)).start();
					} catch (SocketTimeoutException e) {
						// do nothing
					} catch (IOException e) {
						logger.error(e);
					}
				}
				serverSocket.close();
			} catch (IOException e) {
				logger.error(e);
			}
			logger.info("daemon stopped");
		}
		
	}
	
	// Represent a slave lunched by the daemon
	private class Slave implements Runnable {
		
		private Socket socket;
		
		private Slave(Socket socket) {
			this.socket = socket;
		}

		@Override
		public void run() {
			try {
				try {
					ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
					Message message = (Message) ois.readObject();
					
					/*
					 * Gestion des objets sérialisés reçus.
					 * 
					 * BookStore 
					 * 		   Objet permettant d'enregistrer un bookstore
					 * ParcelDelivery
					 * 		   Object permettant d'enregistrer un ParcelDelivery
					 * Catalog
					 * 		   Object envoyé par un bookstore pour enregistrer son catalag
					 * ContratBookStore
					 * 		   Object envoyé par le bookstore host pour conclure un contrat sur un livre
					 * CancelDeliver
					 * 		   Object envoyé par un bookstore pour annuler un contrat sur un livre
					 */
					
					if (message instanceof BookStore) { // enregistrement d'un bookStore
							BookStore bs = (BookStore) message;
							BookStoreHandler.getInstance().addBookStore(bs);
													
							logger.info("Enregistrement du bookStore : " + bs.getName() + "\t "+ bs.getIp() +"\t "+ bs.getPort());
							
							// Contacter le bookstore pour obtenir le catalog
							logger.info("Demande de catalog pour le bookStore");
							LibraryRequest libraryRequest = new LibraryRequest("GETCATALOG");
							Communication.getInstance().async(libraryRequest, bs.getIp(), bs.getPort());	
					} else if (message instanceof ParcelDelivery) {
						ParcelDelivery parceldelivery = (ParcelDelivery) message;
						logger.info("Enregistrement du ParcelDelivery : " + parceldelivery.getName() + "\t " + parceldelivery.getIp() + "\t " + parceldelivery.getPort());
						
						ParcelDeliveryHandler.getInstance().addDelivery(parceldelivery);
						
					} else if (message instanceof Catalog) { // enregistrement d'un catalog
						Catalog catalog = (Catalog) message;
						
						RetreiveAllCatalogs.getInstance().addCatalogue(catalog);
						logger.info("Nouveau Catalogue d'un BookStore");
						
					} else if (message instanceof ContratBookStore) { // Etablissement d'un contat
						ContratBookStore cbs = (ContratBookStore) message;
						AuctionHandler.getInstance().receivedBid(cbs);
					} else if (message instanceof CancelDeliver) {
						logger.info("CANCEL COMMANDE BOOK :: LIBRARY SIDE");
						
						CancelDeliver cd = (CancelDeliver) message;
						
						AuctionHandler.getInstance().cancelBid(cd);
					}

				} catch (ClassNotFoundException e) {
					logger.error(e);
				} catch (ClassCastException e) {
					logger.error(e);
				}
				socket.close();
			} catch (IOException e) {
				logger.error(e);
			}
		}
		
	}

}

